#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/of.h>
#include <linux/of_address.h>




#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define NEWCHRLED_CNT			1		  	/* 设备号个数 */
#define NEWCHRLED_NAME			"nstar_lcd"	/* 名字 */
#define LEDOFF 					0			/* 关灯 */
#define LEDON 					1			/* 开灯 */

#include "gpio.h"


/* 寄存器物理地址 */
#define V3S_GPIO_BASE				(0x01C20800)	


/* 映射后的寄存器虚拟地址指针 */
static void __iomem *V3S_GPIO_REGMEM;
static PIO_Map *PIO = NULL;

#define MAX_RX_LEN 2200
static unsigned char tempbuf[MAX_RX_LEN+4];	
#define delay_us  udelay
#define write_at24c32_addr   0xa0
#define read_at24c32_addr    0xa1

struct gpio_s {
    unsigned int reg0;
    PORT port;
    unsigned int pins;
    PIN_MODE mode;
};

/* newchrled设备结构体 */
struct newchrled_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
    struct device_node	*nd; /* 设备节点 */
    struct gpio_s reg[2];
};

struct newchrled_dev newchrled;	/* led设备 */

static void iic_wrtie(unsigned char *buf, unsigned int count);
static unsigned int iic_read(unsigned char *buf, unsigned int count);


static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &newchrled; /* 设置私有数据 */
	return 0;
}




static void GPIO_ConfigPin(PORT port, unsigned int pin, PIN_MODE mode)
{
    if (PIO == NULL)
        return;
    PIO->Pn[port].CFG[pin / 8] &= ~((unsigned int)0x07 << pin % 8 * 4);
    PIO->Pn[port].CFG[pin / 8] |= ((unsigned int)mode << pin % 8 * 4);
}

static void GPIO_SetPin(PORT port, unsigned int pin, unsigned int level)
{
    if (PIO == NULL)
        return;
    if (level)
       PIO->Pn[port].DAT |= (1 << pin);
    else
       PIO->Pn[port].DAT &= ~(1 << pin);
}




static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	ssize_t ret = 0;
	if(cnt > MAX_RX_LEN)
		cnt= MAX_RX_LEN;
	ret = iic_read(tempbuf, (unsigned int)cnt);

	if(copy_to_user(buf, tempbuf, ret)) {
        return 0;
    }
    return ret;
}


static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
    if(cnt > MAX_RX_LEN)
	    cnt= MAX_RX_LEN;
        
	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}
    printk("iic_write %d %d %d\n", tempbuf[0],  tempbuf[1],  tempbuf[2]);
	iic_wrtie(tempbuf, (unsigned int)retvalue);
	return retvalue;

}



static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备操作函数 */
static struct file_operations newchrled_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};


static void board_led_init(void)
{
    int i;
    V3S_GPIO_REGMEM = ioremap(V3S_GPIO_BASE, sizeof(PIO_Map));
	PIO= (PIO_Map*)V3S_GPIO_REGMEM;

    for(i = 0; i < 2; i++) {
        struct gpio_s *p = &newchrled.reg[i];
        GPIO_ConfigPin(p->port, p->pins, p->mode);
    }

	if (newchrled.major) {		
		newchrled.devid = MKDEV(newchrled.major, 0);
		register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
		printk("register_chrdev_region %d.%d\n", newchrled.major, newchrled.minor);
		
	} 
	else {						
		alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME);	/* 申请设备号 */
		newchrled.major = MAJOR(newchrled.devid);	/* 获取分配号的主设备号 */
		newchrled.minor = MINOR(newchrled.devid);	/* 获取分配号的次设备号 */
		printk("alloc_chrdev_region %d.%d\n", newchrled.major, newchrled.minor);
	}
}

static void board_led_deinit(void)
{
	iounmap(V3S_GPIO_REGMEM);
}

static void __init _config_gpios(const char* path, struct gpio_s *reg) {
    int ret;
    u32 regdata[4];
    struct device_node *nd = of_find_node_by_path(path);
    if(nd == NULL) {
		printk(__FILE__ "node not find!\r\n");
		return;
	} else {
		printk(__FILE__ "node find!\r\n");
	}

    ret = of_property_read_u32_array(nd, "gpios", regdata, 4);
	if(ret < 0) {
		printk("reg property read failed!\r\n");
	} else {
        reg->port = regdata[1];
        reg->pins = regdata[2];
        reg->mode = regdata[3];
        printk("%x %x %x\n", reg->port, reg->pins, reg->mode);
	}
}

static int __init led_init(void)
{
    _config_gpios("/nstarlcd/nlcd_scl", &newchrled.reg[0]);
    _config_gpios("/nstarlcd/nlcd_sda", &newchrled.reg[1]);
	board_led_init();
	
	newchrled.cdev.owner = THIS_MODULE;
	cdev_init(&newchrled.cdev, &newchrled_fops);
	
	/* 3、添加一个cdev */
	cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);

	/* 4、创建类 */
	newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
	if (IS_ERR(newchrled.class)) {
		return PTR_ERR(newchrled.class);
	}

	/* 5、创建设备 */
	newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
	if (IS_ERR(newchrled.device)) {
		return PTR_ERR(newchrled.device);
	}

    printk("led init ok\n");
	return 0;
}


static void __exit led_exit(void)
{
	board_led_deinit();

	/* 注销字符设备驱动 */
	cdev_del(&newchrled.cdev);/*  删除cdev */
	unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */

	device_destroy(newchrled.class, newchrled.devid);
	class_destroy(newchrled.class);
}




static unsigned int GPIO_GetPin(PORT port, unsigned int pin) {
    if (PIO == NULL)
        return 0;
    return (PIO->Pn[port].DAT & (1 << pin) ) ? 1 : 0;
}


#define SCL(value)	GPIO_SetPin(newchrled.reg[0].port, newchrled.reg[0].pins, value)
#define SDA(value)	GPIO_SetPin(newchrled.reg[1].port, newchrled.reg[1].pins, value)
#define SDA_OUT()   GPIO_ConfigPin(newchrled.reg[1].port, newchrled.reg[1].pins, OUT)
#define SDA_IN() 	GPIO_ConfigPin(newchrled.reg[1].port, newchrled.reg[1].pins, IN)
#define READ_SDA    GPIO_GetPin(newchrled.reg[1].port, newchrled.reg[1].pins)


static void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	SDA(1);	  	  
	SCL(1);
	delay_us(4);
 	SDA(0);//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	SCL(0);//钳住I2C总线，准备发送或接收数据 
	delay_us(10);
}	  

static void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	SCL(0);
	SDA(0);//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	SCL(1); 
	delay_us(4);		
	SDA(1);//发送I2C总线结束信号						   	
}

static unsigned char IIC_Wait_Ack(void)
{
	unsigned char ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	SDA(1);delay_us(1);	   
	SCL(1);delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>100)
		{
			IIC_Stop();
			//printk("wait ack faild\n");
			return 1;
		}
	}
	SCL(0);//时钟输出0 	   
	return 0;  
} 

static void IIC_Ack(void)
{
	SCL(0);
	SDA_OUT();
	SDA(0);
	delay_us(2);
	SCL(1);
	delay_us(2);
	SCL(0);
}
  
static void IIC_NAck(void)
{
	SCL(0);
	SDA_OUT();
	SDA(1);
	delay_us(2);
	SCL(1);
	delay_us(2);
	SCL(0);
}					 				     
		  
static void IIC_Send_Byte(unsigned char txd)
{                        
    unsigned char t;   
	SDA_OUT(); 	    
    SCL(0);//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {          
       	SDA((txd&0x80)>>7);
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		SCL(1);
		delay_us(2); 
		SCL(0);	
		delay_us(2);
    }	 
} 	    

static unsigned char IIC_Read_Byte(void)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        SCL(0); 
        delay_us(2);
		SCL(1);
		delay_us(2); 
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(2); 
    }					 
    return receive;
}

static void iic_wrtie(unsigned char *buf, unsigned int count)
{
	IIC_Start();  
	IIC_Send_Byte(write_at24c32_addr);
	IIC_Wait_Ack();
	while(count--){
		IIC_Send_Byte(*buf++);
		IIC_Wait_Ack();
	}
//	IIC_Send_Byte(*buf);
//	IIC_Wait_Ack();
	IIC_Stop();
}

const char CRC8Table[]={
  0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
  157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
  35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
  190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
  70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
  219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
  101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
  248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
  140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
  17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
  175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
  50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
  202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
  87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
  233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
  116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};


unsigned char CRC8_Table(unsigned char *p, unsigned short counter)
{
    unsigned char crc8 = 0;

    for( ; counter > 0; counter--){
        crc8 = CRC8Table[crc8^*p];
        p++;
    }
    return(crc8);

}

static unsigned int iic_read(unsigned char *buf, unsigned int count)
{
	unsigned char temp[4], *p;
	p= temp;
	IIC_Start();
	IIC_Send_Byte(read_at24c32_addr);
	IIC_Wait_Ack();
	temp[0]=IIC_Read_Byte();
	IIC_Ack();
	temp[1]=IIC_Read_Byte();
	IIC_Ack();
	temp[2]=IIC_Read_Byte();
	temp[3]= CRC8_Table(temp, 2);


	if(temp[2] != temp[3] ){
		
		temp[1]=0;
	}
	if(temp[1] > count ){
		printk("too long=%d\n", temp[1]);
		temp[1]= count;
	}
	else
		count= temp[1];
	if(count >0){
		IIC_Ack();
		while(temp[1] > 1){
			temp[1]--;
			*buf++= IIC_Read_Byte();
			IIC_Ack();
		}
		*buf= IIC_Read_Byte();
	}
	IIC_NAck();//Nack结束
	IIC_Stop();
	return count;
}




module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");







